use figment::Figment;
use std::sync::Arc;

use crate::{
    config::Config,
    http,
    listener::Listener,
    listener::ToListener,
    middleware::{Middleware, Next},
    request::Request,
    resource::Resource,
    route::Route,
    router::{Router, Selection},
};

#[derive(Clone)]
pub struct Server {
    figment: Figment,
    router: Arc<Router>,
    resource: Resource,
    middleware: Arc<Vec<Arc<dyn Middleware>>>,
}

impl std::fmt::Debug for Server {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Server").finish()
    }
}

impl Server {
    pub fn at<'a>(&mut self, path: &str) -> Route {
        let router = Arc::get_mut(&mut self.router)
            .expect("Registering routes is not possible after the Server has started");
        Route::new(router, path.to_string())
    }

    pub fn new() -> Self {
        Server {
            router: Arc::new(Router::new()),
            middleware: Arc::new(vec![]),
            resource: Resource::default(),
            figment: Config::figment(),
        }
    }

    ///监听套接字
    pub async fn run(self) -> anyhow::Result<()> {
        let config: Config = self.figment.extract()?;

        let url = format!("{}:{}", config.address, config.port);

        let mut listener = url.to_listener()?;
        listener.bind(self).await?;
        for info in listener.info().iter() {
            log::info!("Server listening on {}", info);
        }
        listener.accept().await?;
        Ok(())
    }

    ///将请求响应转换为响应
    pub async fn respond<Req, Res>(&self, req: Req) -> http::Result<Res>
    where
        Req: Into<http::Request>,
        Res: From<http::Response>,
    {
        let req: http::Request = req.into();
        let Self {
            router,
            middleware,
            resource,
            figment,
            ..
        } = self.clone();

        let method = req.method().to_owned();
        let Selection { endpoint, params } = router.route(&req.url().path(), method);
        let route_params = vec![params];
        let config: Config = figment.extract().unwrap();

        let req = Request::new(req, route_params, resource, config);

        //遍历中间件和handle，返回一个Result<Response>
        let next = Next {
            endpoint,
            next_middleware: &middleware,
        };
        let res: http::Response = next.run(req).await?.into();

        Ok(res.into())
    }
}
